// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#include <gtest/gtest.h>
#include <utility>

#include <pollux/type/floating_point_util.h>

namespace kumo::pollux {
namespace {

template <typename T>
void testFloatingPoint() {
  using namespace util::floating_point;

  static const T kNaN = std::numeric_limits<T>::quiet_NaN();
  static const T kSNAN = std::numeric_limits<T>::signaling_NaN();
  static const T kInf = std::numeric_limits<T>::infinity();

  ASSERT_TRUE(NaNAwareEquals<T>{}(kNaN, kNaN));
  ASSERT_TRUE(NaNAwareEquals<T>{}(kNaN, kSNAN));
  ASSERT_FALSE(NaNAwareEquals<T>{}(kNaN, 0.0));

  ASSERT_FALSE(NaNAwareLessThan<T>{}(kNaN, kNaN));
  ASSERT_FALSE(NaNAwareLessThan<T>{}(kNaN, kSNAN));
  ASSERT_FALSE(NaNAwareLessThan<T>{}(kNaN, kInf));
  ASSERT_TRUE(NaNAwareLessThan<T>{}(kInf, kNaN));

  ASSERT_TRUE(NaNAwareLessThanEqual<T>{}(kNaN, kNaN));
  ASSERT_TRUE(NaNAwareLessThanEqual<T>{}(kNaN, kSNAN));
  ASSERT_TRUE(NaNAwareLessThanEqual<T>{}(kInf, kNaN));
  ASSERT_TRUE(NaNAwareLessThanEqual<T>{}(0.0, kInf));
  ASSERT_FALSE(NaNAwareLessThanEqual<T>{}(kNaN, kInf));
  ASSERT_FALSE(NaNAwareLessThanEqual<T>{}(kNaN, 0.0));

  ASSERT_FALSE(NaNAwareGreaterThan<T>{}(kNaN, kNaN));
  ASSERT_FALSE(NaNAwareGreaterThan<T>{}(kNaN, kSNAN));
  ASSERT_FALSE(NaNAwareGreaterThan<T>{}(kInf, kNaN));
  ASSERT_TRUE(NaNAwareGreaterThan<T>{}(kNaN, kInf));

  ASSERT_TRUE(NaNAwareGreaterThanEqual<T>{}(kNaN, kNaN));
  ASSERT_TRUE(NaNAwareGreaterThanEqual<T>{}(kNaN, kSNAN));
  ASSERT_FALSE(NaNAwareGreaterThanEqual<T>{}(kInf, kNaN));
  ASSERT_FALSE(NaNAwareGreaterThanEqual<T>{}(0.0, kInf));
  ASSERT_TRUE(NaNAwareGreaterThanEqual<T>{}(kNaN, kInf));
  ASSERT_TRUE(NaNAwareGreaterThanEqual<T>{}(kNaN, 0.0));

  ASSERT_EQ(NaNAwareHash<T>{}(kNaN), NaNAwareHash<T>{}(kSNAN));
  ASSERT_EQ(NaNAwareHash<T>{}(kNaN), NaNAwareHash<T>{}(kNaN));
  ASSERT_EQ(NaNAwareHash<T>{}(0.0), NaNAwareHash<T>{}(0.0));
  ASSERT_EQ(NaNAwareHash<T>{}(kInf), NaNAwareHash<T>{}(kInf));
  ASSERT_NE(NaNAwareHash<T>{}(kNaN), NaNAwareHash<T>{}(kInf));
  ASSERT_NE(NaNAwareHash<T>{}(kNaN), NaNAwareHash<T>{}(0.0));
}

TEST(FloatingPointUtilTest, basic) {
  testFloatingPoint<float>();
  testFloatingPoint<double>();
}
} // namespace
} // namespace kumo::pollux
